assume cs:code 
 
data segment 
	db	'Welcome to masm!',  0     ;“$”本身并不显示，只起到边界的作用
data ends 

code segment
    start:
        mov dh,10 ;行号
        mov dl,10 ;列号
        mov cl,2 ;颜色

        call copy_new_int7ch ;安装程序
        call set_new_int7ch  ;设置程序入口
        
        mov ax,data
        mov ds,ax            ;ds:[si]指向字符串。
        mov si,0
        
        int 7ch ;测试
        
        mov ax,4c00h
        int 21h
   
    set_new_int7ch:
        push dx ;入栈保护
        push cx

        mov ax,0
        mov es,ax
        
        mov word ptr es:[7ch*4], 200h
        mov word ptr es:[7ch*4+2], 0

        pop cx
        pop dx

        ret
    copy_new_int7ch:
        push dx
        push cx

        mov ax,cs
        mov ds,ax
        mov si,offset show_str					;设置ds:si指向源地址

        mov ax,0
        mov es,ax
        mov di,200h							;设置es:di指向目的地址
        
        mov cx,offset show_str_end - show_str	;设置cx为传输长度
        cld									;设置传输方向为正
        rep movsb

        pop cx
        pop dx

        ret
    ;-----------------------------------------
    ;show_str功能 :按行和列及字符属性显示字符串  
    ;入口参数：dh-行数、dl-列数、cl-字符属性、ds:[si]指向字符串。
    ;返回值：无
    ;-----------------------------------------
    show_str: 
        push dx
        push cx
        push si ;数据入栈保护

        mov ax,0B800H ;在汇编源程序中，数据不能以字母开头，所以要在前面加0。
        mov es,ax     ;显示缓冲区的段地址

        ;计算dl * 2 
        mov ax,0
        mov al,2
        mul dl
        mov bx,ax ;把结果放到dl

        ; 计算 dh * 160 结果在ax中
        mov ax,0   ;（ax）= 0，防止高位不为零  
        mov al,160 ;0a0H
        mul dh
        add bx,ax  ;获取最终否偏移量

        mov di,0   ;di作为字符的偏移零
        mov al,cl  ; 将属性放到al中，因为后边的程序会用到cx
    read:
        mov cl,ds:[si]
        mov ch,0             ;cx中 ch固定为零，关键在于cl的变化是否为零 
        jcxz ok              ;跳出，字符串操作完毕
        mov es:[bx+di+0],cl  ;对字符串业务操作 写入字符
        mov es:[bx+di+1],al  ; 写入属性
        inc si               ;下一个字符串
        add di,2        
        jmp short read       ;继续读取字符串
    ok:
        pop si 
        pop cx
        pop dx

		iret  ;CPU执行int 7ch指令进入中断例程之前，标志寄存器、当前的CS和IP被压入栈
		      ;在执行完中断例程后，应该用iret 指令恢复int 7ch执行前的标志寄存器和CS、IP的
    show_str_end: nop                                            
        
code ends
end start